home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: delta / whiteline CD Series - delta.iso / tools / lernen / dicionar / source / btree.mod < prev    next >
Encoding:
Modula Implementation  |  1995-11-25  |  32.5 KB  |  1,053 lines

  1.  IMPLEMENTATION MODULE BTree;
  2. (* FROM InOut IMPORT WriteString, WriteInt, WriteLn, WriteCard;
  3.  FROM NumIO IMPORT WriteLong; *)
  4.  FROM SYSTEM IMPORT TSIZE;
  5.  IMPORT GEMDOS,FileSystem;
  6.  
  7. VAR    Tree : PagePtr; 
  8. (* Globale Variable um festzustellen ob ich irgendeinen Eintrag *)
  9. (* In meiner Datei habe *)
  10.  
  11.  
  12.  
  13. PROCEDURE Get( VAR File : FileOf; Pos : LONGINT);
  14. VAR Position : LONGINT;
  15. BEGIN
  16.    IF Pos <= File.Last THEN
  17.        IF Pos >= 0D THEN
  18.         Position := Pos * File.RecordSize;
  19.         GEMDOS.Seek(Position,File.Handle,GEMDOS.beginning,Position);
  20.         GEMDOS.Read(File.Handle,File.RecordSize,File.RecordPtr);
  21.         File.Current := Pos;
  22.     ELSE (*HALT*);    
  23.     END(*IF*);    
  24.    ELSE
  25.      (*HALT*);
  26.    END(*IF*); 
  27. END Get;
  28.  
  29. PROCEDURE Put(VAR File : FileOf; Pos : LONGINT);
  30. VAR   Position : LONGINT;
  31. BEGIN
  32.   IF Pos <= File.Last+1D  THEN
  33.      IF Pos >= 0D THEN
  34.          Position := Pos * File.RecordSize;
  35.          GEMDOS.Seek(Position,File.Handle,GEMDOS.beginning,Position);
  36.          GEMDOS.Write(File.Handle,File.RecordSize,File.RecordPtr);
  37.          File.Current := Pos;
  38.      ELSE (*HALT*);    
  39.      END(*IF*);    
  40.   ELSE
  41.     (*HALT*);
  42.   END(*IF*);  
  43. END Put;
  44.  
  45. PROCEDURE FileLength(FileName : ARRAY OF CHAR; VAR length : LONGINT) :BOOLEAN;
  46. VAR f : FileSystem.File;
  47.  
  48. BEGIN
  49.     FileSystem.Lookup(f,FileName,FALSE);
  50.     FileSystem.Length(f,length);
  51.     IF f.errorno # 0 THEN RETURN FALSE END(*IF*);
  52.     RETURN length > 0D;
  53. END FileLength;
  54.  
  55. PROCEDURE Create(VAR File : FileOf);
  56. VAR f : FileSystem.File;
  57.  
  58. BEGIN
  59.     FileSystem.Lookup(f, File.Name, TRUE);
  60.     FileSystem.Close(f);
  61. END Create;
  62.  
  63. PROCEDURE Start(VAR File : FileOf);
  64. VAR Length : LONGINT;
  65.  
  66. BEGIN
  67.    File.Last:=-1D;
  68.    IF FileLength(File.Name,Length) THEN
  69.       File.Last:= Length DIV File.RecordSize -1D;
  70.    END(*IF*);
  71.    GEMDOS.Open(File.Name,2,File.Handle);
  72.    IF (File.Last = Empty) THEN
  73.       Tree := Empty;
  74.    ELSE
  75.       Tree:= TreeRoot;
  76.    END(*IF*);
  77.    File.Current := Tree;
  78. END Start;
  79.  
  80. PROCEDURE Close(VAR File : FileOf);
  81. VAR OK : BOOLEAN;
  82. BEGIN
  83.    OK:=GEMDOS.Close(File.Handle);
  84.    IF ~OK THEN (*HALT*); END(*IF*);
  85.    Tree := Empty;
  86.    File.Current := Tree;
  87. END Close;
  88.  
  89. PROCEDURE PutData(VAR DataBase : FileOf; Data : DataType;  Reference : DataPtr);
  90. BEGIN
  91.     Get(DataBase,Reference);
  92.     DataBase.RecordPtr^.Flag := TRUE;
  93.     DataBase.RecordPtr^.Data := Data;
  94.     Put(DataBase,Reference);
  95. END PutData;
  96.  
  97. PROCEDURE GetData(VAR DataBase : FileOf; VAR Data : DataType; VAR Keys : KeyArray; Reference :DataPtr);
  98. BEGIN
  99.    Get(DataBase,Reference);
  100.    Data := DataBase.RecordPtr^.Data;
  101.    Keys := DataBase.RecordPtr^.Key;
  102. END GetData;
  103.  
  104. PROCEDURE AddData( VAR DataBase : FileOf; NewData : DataType) : DataPtr;
  105. BEGIN
  106.     DataBase.Last := DataBase.Last +1D;
  107.     DataBase.RecordPtr^.Data := NewData;
  108.     Put ( DataBase, DataBase.Last);
  109.     RETURN DataBase.Last;
  110. END AddData;
  111.  
  112. PROCEDURE AddKey(VAR Index, DataBase : FileOf; NewKey : KeyType; Reference : DataPtr): BOOLEAN;
  113.  
  114. VAR h : BOOLEAN;
  115.     u : ItemType;
  116.     PROCEDURE search( x : KeyType;
  117.               a : PagePtr;
  118.          VAR  h : BOOLEAN;
  119.          VAR  v : ItemType): BOOLEAN;
  120. (* Suche Schlüssel x im B-Baum mit Wurzel a; falls nicht vorhanden füge 
  121.  ein Element mit Schlüssel x in den Baum ein. Ein Element, das auf eine
  122.  tiefere Stufe zu bingen ist, ist v zuzuweisen; h := Baum ist gewachsen *)
  123.           
  124.  
  125.     VAR k, l, r : INTEGER;
  126.         u : ItemType;
  127.  
  128.  
  129.  
  130.  
  131.         PROCEDURE insert;
  132.  
  133.         VAR i : INTEGER;
  134.         b : PageType;
  135.  
  136.         BEGIN (* insert *)
  137.           Get(Index , a); (* füge u rechts von Index^.Item[r] ein *)
  138.           WITH Index.RecordPtr1^ DO
  139.          IF Anz < nn THEN
  140.             (*INC(Anz,1);*)
  141.             Anz := Anz +1;
  142.             h:= FALSE;
  143.             FOR i := Anz TO r+2 BY -1 DO
  144.             Item[i]:= Item[i-1];
  145.             END(*FOR*);
  146.             Item[r+1] := u;
  147.             Put (Index,a);
  148.          ELSE (* Seitenüberlauf! Teile Seite auf
  149.                und weise v das herausfallende Element zu *)
  150.          IF r<=n THEN
  151.                IF r=n THEN
  152.               v:=u
  153.                ELSE
  154.              v:=Item[n];
  155.              FOR i:= n TO r+2 BY -1 DO
  156.                 Item[i]:=Item[i-1];
  157.              END(*FOR*);
  158.              Item[r+1] :=u;
  159.             END;
  160.              FOR i:= 1 TO n DO
  161.             b.Item[i]:=Item[i+n];
  162.              END(*FOR*);
  163.          ELSE (* füge u in die rechte Seite ein *)
  164.             r:= r-n;
  165.             v:= Item[n+1];
  166.             FOR i := 1 TO r-1 DO
  167.             b.Item[i]:=Item[i+n+1];
  168.             END(*FOR*);
  169.             b.Item[r]:=u;
  170.             FOR i:=r+1 TO n DO
  171.             b.Item[i]:=Item[i+n];
  172.             END(*FOR*);
  173.          END;
  174.          Anz := n;
  175.          Put (Index,a);
  176.          b.Anz:=n;
  177.          b.Ptr0:= v.Ptr;
  178.          Index.RecordPtr1^:=b;
  179.          Index.RecordPtr1^.Flag:= TRUE;
  180.          Index.Last:=Index.Last+1D;
  181.          (*INC(Index.Last,1);*)
  182.          Put( Index, Index.Last);
  183.          v.Ptr:=Index.Last;
  184.            END(*IF*);
  185.           END;(*with*)
  186.         END insert;
  187.  
  188.     BEGIN (* search *)
  189.     (* suche Schlüssel x auf Seite a; NOT h *)
  190.     
  191.       IF a = Empty THEN (* rekursive Abruchbedingung *)
  192.                         (* Element mit Schlüssel x ist nicht im Baum *)
  193.         h:= TRUE; (* -> auf höherer Seite einfügen! *)
  194.         WITH v DO
  195.           Key:=x;
  196.           Ptr := Empty;
  197.           RecordNr:= DataBase.Last
  198.         END;(*with*)
  199.       ELSE
  200.          Get(Index,a);
  201.          WITH Index.RecordPtr1^ DO
  202.            l:=1; (* Binäre Suche auf der Seite *)
  203.            r:= Anz;
  204.            REPEAT
  205.          k:=(l+r) DIV 2;
  206.          IF x <= Item[k].Key THEN
  207.             r:= k-1;
  208.          END(*IF*);
  209.          IF x >=Item[k].Key THEN
  210.             l:= k+1;
  211.          END(*IF*);
  212.            UNTIL r < l;
  213.            IF (l-r >1) THEN  (* Element gefunden ! *)
  214.                  (* -> kein Änderung am B-Baum *)
  215.                  (* Neues Element in die NachfolgerListe  *)
  216.             IF Index.MultipleKeys THEN         
  217.             (* Das letzte eingefügte Element ist das erste in der Nachfolgerliste *)
  218.             (* Dies ist so um möglichst wenig Plattenzugriffe zu haben *)
  219.                       Get(DataBase,Item[k].RecordNr);
  220.                       DataBase.RecordPtr^.Prev[Index.Type] := Reference;
  221.                       Put(DataBase,Item[k].RecordNr);
  222.               Get(DataBase,Reference);
  223.               DataBase.RecordPtr^.Next[Index.Type] := Item[k].RecordNr;
  224.               Put(DataBase,Reference);
  225.               Item[k].RecordNr := Reference;
  226.               Put(Index,a);
  227.               h:= FALSE;
  228.              ELSE
  229.                 (*HALT*); (* Eintrag gefunden obwohl doppelte Schlüssel nicht erlaubt sind *)
  230.                 RETURN FALSE
  231.              END(*IF*);      
  232.            ELSE
  233.          IF r=0 THEN (* Element nicht gefunden *)
  234.                   (* -> Suche geht rekursiv weiter *)
  235.            IF  ~search(x,Ptr0,h,u) THEN
  236.                RETURN FALSE
  237.            END(*IF*);
  238.          ELSE
  239.             IF ~search(x,Item[r].Ptr,h,u) THEN 
  240.                 RETURN FALSE
  241.             END(*IF*);    
  242.          END(*IF*);
  243.          IF h THEN (* Element einfügen *)
  244.             insert;
  245.          END(*IF*);
  246.            END(*if*);
  247.          END; (*with*)
  248.        END; (*if*)
  249.      RETURN TRUE  
  250.      END search;
  251.  
  252. BEGIN (*insert data*)
  253.     Get(DataBase,Reference);
  254.     WITH DataBase.RecordPtr^ DO (* DatenPuffer initialisieren  *)
  255.        Flag := TRUE;
  256.        IF Index.MultipleKeys THEN
  257.            Next[Index.Type] := Empty;
  258.            Prev[Index.Type] := Empty;
  259.        END(*IF*);       
  260.        Key[Index.Type] := NewKey;
  261.     END; (*with*)
  262.     Put(DataBase, Reference);
  263.     IF Index.Last=Empty THEN
  264.        Tree := Empty;
  265.     END(*IF*);   
  266.     IF search( NewKey, Tree, h, u) THEN
  267.         IF h THEN (* Nach dem Aufruf von search kann sich noch ein Element *)
  268.               (* in v befinden. Dieses Element wird dann zur Wurzel des *)
  269.               (* Baumes *)
  270.         IF Index.Last # Empty THEN
  271.            Get(Index,0D);
  272.         END(*IF*);
  273.         Index.RecordPtr1^.Flag := TRUE;
  274.         Index.Last:= Index.Last+1D;
  275.         (*INC(Index.Last,1);*)
  276.         Put (Index, Index.Last);
  277.         Tree:= TreeRoot;
  278.         WITH Index.RecordPtr1^ DO
  279.            Anz:=1;
  280.            IF Index.Last =0D THEN
  281.               Ptr0:=-1D;
  282.            ELSE
  283.               Ptr0 := Index.Last;
  284.            END(*IF*);
  285.            Item[1]:=u;
  286.         END;(*with*)
  287.         Put(Index,TreeRoot);(* Wurzel befindet sich immer an Position 0 *)
  288.                     (* der Datei ! *)
  289.         END; (*if*)
  290.         RETURN TRUE
  291.     ELSE
  292.         (*HALT*);
  293.         RETURN FALSE
  294.     END(*IF*);    
  295. END AddKey;
  296.  
  297. PROCEDURE Delete(VAR Index, DataBase : FileOf; Key : KeyType; VAR DataPointer : DataPtr): BOOLEAN;
  298. (* Suche und Lösche Schlüssel Key im B-Baum; tritt Unterlauf ein so gleiche
  299.   falls möglich mit benachbarter Seite aus, sonst lege mit ihr zusammen;
  300.   h:= 'Seite ist nicht voll genug ', d.h. m<n *)
  301.  
  302. VAR DelList : DataPtr;
  303.        q : PagePtr;
  304.        h, DoDeleteData : BOOLEAN;
  305.  
  306.   PROCEDURE search (   x : KeyType;
  307.                a : PagePtr;
  308.            VAR h : BOOLEAN);
  309.  
  310.     VAR q : PagePtr;
  311.     i,
  312.     k,
  313.     l,
  314.     r: (*INTEGER;*) CARDINAL;
  315.         Next, Previous : DataPtr;
  316.    PROCEDURE underflow( c,
  317.             a : PagePtr;
  318.             s : CARDINAL;
  319.             VAR h : BOOLEAN);
  320.  
  321. (* a = Seite mit Unterlauf, c = Vorgängerseite *)
  322.    VAR b : PagePtr;
  323.        i,
  324.        k,
  325.        mb,
  326.        mc : CARDINAL;
  327.        ic,
  328.        ia : PageType;
  329.  
  330.        BEGIN (* underflow *)
  331.         Get(Index,c);
  332.         ic:= Index.RecordPtr1^;
  333.         Get (Index,a);
  334.         ia := Index.RecordPtr1^;
  335.         mc := ic.Anz;  
  336.         IF s<mc THEN  (* b := rechts von a *)
  337.           s:= s+1;
  338.           (*INC(s,1);*)
  339.           b:=ic.Item[s].Ptr;
  340.           Get (Index,b);
  341.           mb := Index.RecordPtr1^.Anz;
  342.           k:=(mb-n+1)  DIV 2;
  343.           ia.Item[n]:=ic.Item[s];
  344.           ia.Item[n].Ptr :=Index.RecordPtr1^.Ptr0;
  345.           IF k>0 THEN (* bringe k Elemente von b nach a *)
  346.           (* Ausgleich zwischen den Seiten *)
  347.              FOR i := 1 TO k-1 DO
  348.              ia.Item[i+n]:= Index.RecordPtr1^.Item[i];
  349.              END(*FOR*);
  350.              ic.Item[s]:=Index.RecordPtr1^.Item[k];
  351.              ic.Item[s].Ptr := b;
  352.              Index.RecordPtr1^.Ptr0:= Index.RecordPtr1^.Item[k].Ptr;
  353.              mb := mb-k;
  354.              FOR i:=1 TO mb DO
  355.              Index.RecordPtr1^.Item[i]:= Index.RecordPtr1^.Item[i+k];
  356.              END(*FOR*);
  357.              Index.RecordPtr1^.Anz := mb;
  358.              ia.Anz := n-1+k;
  359.              h:= FALSE;
  360.           ELSE (* mische Seiten a und b *)
  361.           (*Seiten werden zu einer Seite zusammengefasst *)
  362.              FOR i:= 1 TO n DO
  363.             ia.Item[i+n] := Index.RecordPtr1^.Item[i];
  364.              END(*FOR*);
  365.              FOR i:= s TO (mc-1) DO
  366.              ic.Item[i]:= ic.Item[i+1];
  367.              END(*FOR*);
  368.              ia.Anz := nn;
  369.              ic.Anz := mc-1;
  370.              h:= mc<=n;
  371.              Index.RecordPtr1^.Flag:= FALSE;
  372.           END (*if*)
  373.         ELSE (* b:= Seite links von a *)
  374.           IF s=1 THEN
  375.              b:=ic.Ptr0
  376.           ELSE
  377.             b:= ic.Item[s-1].Ptr;
  378.           END(*IF*);
  379.           Get(Index,b);
  380.           mb := Index.RecordPtr1^.Anz+1;
  381.           k:=(mb-n) DIV 2;
  382.           IF k>0 THEN (* bringe k Elemente von b nach a *)
  383.           (* Ausgleich zwischen denSeiten *)
  384.             FOR i := n-1 TO 1 BY -1 DO
  385.                ia.Item[i+k]:=ia.Item[i];
  386.             END(*FOR*);
  387.             ia.Item[k]:=ic.Item[s];
  388.             ia.Item[k].Ptr := ia.Ptr0;
  389.             mb:=mb-k;
  390.             FOR i := k-1 TO 1 BY -1 DO
  391.             ia.Item[i] := Index.RecordPtr1^.Item[i+mb];
  392.             END(*FOR*);
  393.             ia.Ptr0 := Index.RecordPtr1^.Item[mb].Ptr;
  394.             ic.Item[s]:= Index.RecordPtr1^.Item[mb];
  395.             ic.Item[s].Ptr := a;
  396.             Index.RecordPtr1^.Anz:=mb-1;
  397.             ia.Anz:= n-1 +k;
  398.             h := FALSE;
  399.           ELSE (* mische Seiten a und b *)
  400.           (* Seiten werden zusammngefasst *)
  401.               Index.RecordPtr1^.Item[mb]:=ic.Item[s];
  402.               Index.RecordPtr1^.Item[mb].Ptr:= ia.Ptr0;
  403.               FOR i:=1 TO n-1 DO
  404.               Index.RecordPtr1^.Item[i+mb]:=ia.Item[i];
  405.               END(*FOR*);
  406.               Index.RecordPtr1^.Anz := nn;
  407.               ic.Anz := mc-1;
  408.               h:=mc<=n;
  409.               ia.Flag:= FALSE;
  410.           END; (*if*)
  411.         END; (*if*)
  412.         Put(Index,b);
  413.         Index.RecordPtr1^:=ia;
  414.         Put(Index,a);
  415.         Index.RecordPtr1^:=ic;
  416.         Put(Index,c);
  417.     END underflow;
  418.  
  419.     PROCEDURE del( p : PagePtr;
  420.            VAR h : BOOLEAN );
  421.  
  422.     VAR q : PagePtr;
  423.        ip : PageType;
  424.         
  425.     BEGIN (*del*)
  426.        Get (Index,p);
  427.        ip := Index.RecordPtr1^;
  428.        WITH ip DO
  429.            q:=Item[Anz].Ptr;
  430.            IF q # Empty THEN
  431.            del(q,h);
  432.            IF h THEN
  433.               underflow(p,q,Anz,h);
  434.            END(*IF*);
  435.         ELSE
  436.            (* k- ter Eintrag des Elements a durch den letzten Eintrag *)
  437.            (* der ermittelten Blattseite p ersetzen *)
  438.            Get(Index,a);
  439.            Item[Anz].Ptr:=Index.RecordPtr1^.Item[k].Ptr;
  440.            Index.RecordPtr1^.Item[k]:=Item[Anz];
  441.            Anz:=Anz-1;
  442.            h:= Anz<n;
  443.            Put(Index,a);
  444.            Index.RecordPtr1^:= ip;
  445.            Put (Index,p);
  446.         END; (*if*)
  447.        END; (*with*)
  448.      END del;
  449.  
  450.      BEGIN (* search *)
  451.          IF a=Empty THEN (* Element nicht gefunden ->  Fertig ! *)
  452.         h:= FALSE;
  453.         DelList:= Empty;
  454.         DoDeleteData := FALSE;
  455.          ELSE
  456.         Get (Index,a);
  457.         WITH Index.RecordPtr1^ DO
  458.            l:=1;
  459.            r:= Anz;
  460.            REPEAT
  461.               k:=(l+r) DIV 2;
  462.               IF x <= Item[k].Key THEN
  463.               r:= k-1;
  464.               END(*IF*);
  465.               IF x >= Item[k].Key THEN
  466.              l:= k+1;
  467.               END(*IF*);
  468.             UNTIL r<l;
  469.             IF r=0 THEN
  470.               q:= Ptr0
  471.             ELSE
  472.               q:= Item[r].Ptr;
  473.             END(*IF*);
  474.             IF l-r>1 THEN (* Eintrag gefunden *)
  475.                DelList:= Item[k].RecordNr;
  476.                IF Index.MultipleKeys THEN
  477.                  LOOP (* Achtung falls für die Variable DataPointer Unsinn Übergeben wurde passiert hier auch Unsinn *)
  478.                     IF  DelList # DataPointer THEN (* Der zu löschende Record stimmt nicht mit dem gefundenen überein *)
  479.                         Get(DataBase,DelList);
  480.                         DelList := DataBase.RecordPtr^.Next[Index.Type];
  481.                      ELSE (* Ich hab den gesuchten Record gefunden *)
  482.                         Get(DataBase,DelList);(* Den Record zwecks Veränderung laden *)
  483.                         IF DataBase.RecordPtr^.Prev[Index.Type] # Empty THEN 
  484.                              (* Der Record hat einen Vorgänger, muß also 
  485.                               nur aus der Verkettung der Datensätzte 
  486.                               untereinander gelöst werden *)
  487.                               Next := DataBase.RecordPtr^.Next[Index.Type];
  488.                               Previous := DataBase.RecordPtr^.Prev[Index.Type];
  489.                               Get(DataBase,Previous);
  490.                               DataBase.RecordPtr^.Next[Index.Type] := Next;
  491.                               Put(DataBase,Previous);
  492.                               IF Next # Empty THEN 
  493.                                 (* Den Nächsten Record an den (jetzt aktuellen) anfügen *)
  494.                                 Get(DataBase,Next);
  495.                                 DataBase.RecordPtr^.Prev[Index.Type] := Previous;
  496.                                 Put(DataBase,Next)
  497.                               END(*IF*);  
  498.                            END(*IF*);
  499.                         EXIT;
  500.                      END(*IF*);      
  501.                      IF DelList = Empty THEN
  502.                         (*HALT*); (* Alle Nachfolger wurden durchlaufen aber kein Eintrag gefunden der den Spezifikationen entspricht *)
  503.                               (* Bedingung hierfür siehe Anfang des LOOPs *)
  504.                         EXIT;
  505.                      END(*IF*);
  506.                   END(*LOOP*);
  507.                   
  508.                   IF DataPointer = Item[k].RecordNr THEN (* Der Erste Eintrag unter diesem Schlüssel war auch der Gesuchte *)
  509.                      Get(DataBase,Item[k].RecordNr);
  510.                      IF DataBase.RecordPtr^.Next[Index.Type] # Empty THEN (* und er hat auch einen Nachfolger *)
  511.                                Item[k].RecordNr :=DataBase.RecordPtr^.Next[Index.Type]; (* Nur den Zeiger von diesem Eintrag auf den nächsten umbiegen *)
  512.                              Put(Index,a);
  513.                              Get(DataBase,Item[k].RecordNr);(* Verweiß auf den jetzt gelöschten Vorgänger entfernen *)
  514.                              DataBase.RecordPtr^.Prev[Index.Type] := Empty;
  515.                              Put(DataBase,Item[k].RecordNr);
  516.                              h:= FALSE;
  517.                              RETURN
  518.                      END(*IF*);    
  519.                   ELSE (* Der erste Eintrag unter diesem Schlüssel war nicht unser gesuchter *)
  520.                       (* (Die Verkettung der Datensätze untereinander haben wir schon im LOOP geändert) *)
  521.                       h := FALSE;
  522.                       RETURN
  523.                   END(*IF*);        
  524.                 END(*IF*);  
  525.               IF q = Empty THEN (* Ausfügen aus einer Blatt-seite *)
  526.               Anz := Anz -1;
  527.               h:= Anz < n;
  528.               FOR i:= k TO Anz DO
  529.                   Item[i]:= Item[i+1];
  530.                END(*FOR*);
  531.                Put (Index,a);
  532.                ELSE
  533.               del(q,h);
  534.               IF h THEN
  535.                  underflow(a,q,r,h);
  536.               END(*IF*);
  537.                END; (*if*)
  538.             ELSE
  539.                search(x,q,h);
  540.                IF h THEN
  541.               underflow(a,q,r,h);
  542.                END(*IF*);
  543.             END; (*if*)
  544.         END(*WITH*);
  545.          END(*IF*);
  546.        END search;
  547.  
  548.  BEGIN (* delete_data *)
  549.     IF Tree # Empty THEN
  550.        DoDeleteData:= TRUE;
  551.        search(Key,Tree,h);
  552.        IF h THEN
  553.       Get (Index,Tree);
  554.       IF Index.RecordPtr1^.Anz =0 THEN
  555.            IF Index.RecordPtr1^.Ptr0 = Empty THEN
  556.            (* Baum ist völlig entleert *)
  557.           Tree := Empty;
  558.           Index.RecordPtr1^.Flag := FALSE;
  559.           Put(Index,TreeRoot);
  560.         ELSE
  561.         (* Bei einem linken Nachfolger wird dieser zur Wurzelseite *)
  562.         (* q und Tree werden physikalisch ausgetauscht ! *)
  563.            q:= Index.RecordPtr1^.Ptr0;
  564.            Get(Index,q);
  565.            Put(Index,Tree);
  566.            Index.RecordPtr1^.Flag:= FALSE;
  567.            Put(Index,q);
  568.         END; (*if*)
  569.        END(*IF*);
  570.      END;(*if h *)
  571.      DataPointer := DelList;
  572.      IF DelList # Empty THEN
  573.      (* Dateiseiten des gelöschten Schlüssels aus Datenbank entfernen*)
  574.          Get(DataBase,DelList);
  575.          DataBase.RecordPtr^.Flag:=FALSE;
  576.          Put(DataBase,DelList);
  577.        (* DelList:= DataBase.RecordPtr^.Next[Index.Type]; END; (*while*) *)
  578.       ELSE
  579.          RETURN FALSE (* Der Zeiger ist leer *)
  580.       END(*IF*);
  581.     END; (*if*)
  582.     RETURN DoDeleteData
  583. END Delete;
  584.  
  585.  
  586. PROCEDURE SearchPtr(VAR Index, DataBase : FileOf; Page : PagePtr; Key : KeyType; VAR found : BOOLEAN) : DataPtr;
  587. (* sucht die Random-Acess-Adresse eines durch Key bezeichneten Datenbankeintrags *)
  588.     VAR q           : PagePtr;
  589.         l, r, k    : CARDINAL;
  590.         SearchPtr0 : DataPtr;
  591.  
  592. BEGIN (* seach_ptr*)
  593.    IF Tree = Empty THEN
  594.        found := FALSE; (* Die Datei ist leer, es gibt nichts was man finden könnte *)
  595.        RETURN Empty;
  596.    END(*IF*);    
  597.    IF Page = Empty THEN
  598.       found := FALSE;(* Auf einer leeren Seite kann man nichts finden *)
  599.       RETURN Empty;
  600.    ELSE
  601.       Get(Index,Page);
  602.       WITH Index.RecordPtr1^ DO
  603.      l:=1;
  604.      r:= Anz;
  605.      REPEAT
  606.         k := (l+r) DIV 2;
  607.         IF Key <= Item[k].Key THEN
  608.            r := k-1;
  609.         END(*IF*);
  610.         IF Key >= Item[k].Key THEN
  611.           l:= k+1;
  612.         END(*IF*);
  613.      UNTIL r<l;
  614.      IF r=0 THEN
  615.         q:= Ptr0
  616.      ELSE
  617.         q:=Item[r].Ptr;
  618.      END(*IF*);
  619.      IF (l-r)>1 THEN
  620.         found := TRUE;
  621.         RETURN Item[k].RecordNr
  622.      ELSE
  623.         SearchPtr0:= SearchPtr(Index,DataBase,q,Key,found);
  624.         IF SearchPtr0 = Empty THEN
  625.           IF r=0 THEN
  626.              SearchPtr0:= Item[1].RecordNr; 
  627.              (* Das ist zwar nicht ganz korrekt        *)
  628.              (* (Der nächst größere Eintrag steht      *)
  629.              (* wahrscheinlich auf einer höheren Seite) *)
  630.              (* aber so spar ich mir etwas Arbeit      *)
  631.           ELSE
  632.              SearchPtr0:= Item[r].RecordNr;
  633.              (* Nächst größerer Eintrag *)
  634.           END(*IF*);
  635.           found := FALSE;
  636.         END(*IF*);        
  637.         RETURN SearchPtr0
  638.      END(*IF*);
  639.      END; (* with*)
  640.    END; (*if*)
  641. END SearchPtr;
  642.  
  643.  
  644.  
  645. PROCEDURE SearchFirst(VAR Index, DataBase : FileOf;  Key : KeyType; 
  646.                       VAR Data : DataType; VAR Keys : KeyArray): BOOLEAN;
  647. (* Sucht den ersten Datensatz mit bezeichneten Schlüssel in der Datenbank *)
  648. VAR NextList : DataPtr;
  649.     found : BOOLEAN;
  650. BEGIN
  651.    NextList:=SearchPtr(Index,DataBase,Tree,Key,found);
  652.    IF NextList # Empty THEN
  653.       Get (DataBase,NextList);
  654.       Data := DataBase.RecordPtr^.Data;
  655.       Keys := DataBase.RecordPtr^.Key;
  656.    END; (*if*)
  657.    RETURN found
  658. END SearchFirst;
  659.  
  660. PROCEDURE SearchNext(VAR Index, DataBase : FileOf; VAR Data : DataType; VAR Keys : KeyArray) : BOOLEAN;
  661. (* Sucht den nächsten Eintrag mit gleichem Schlüssel *)
  662.  VAR NextList : DataPtr; 
  663. BEGIN
  664.      IF Index.MultipleKeys THEN
  665.            NextList := DataBase.RecordPtr^.Next[Index.Type];   
  666.            IF DataBase.Current = NextList THEN
  667.               (* Falls der Eintrag auf sich selber zeigt *)
  668.               (* kann vorkommen ! *)
  669.               RETURN FALSE 
  670.               (* Leider sind falsche Verkettungen über mehrere Einträge *)
  671.               (* 1 -> 2 -> 3 -> 1 oder ähnlich nicht so leicht abzufangen *)
  672.               (* dafür kommen sie aber auch so gut wie nie vor. *)
  673.            END(*IF*);   
  674.        IF NextList # Empty THEN
  675.           Get(DataBase,NextList);
  676.           Data := DataBase.RecordPtr^.Data;
  677.               Keys := DataBase.RecordPtr^.Key;
  678.           RETURN TRUE
  679.        ELSE
  680.           RETURN FALSE
  681.        END; (*if*)
  682.       ELSE
  683.           RETURN FALSE
  684.       END(*IF*);   
  685. END SearchNext;
  686.  
  687. PROCEDURE SearchPrevious(VAR Index, DataBase : FileOf; VAR Data : DataType;VAR Keys : KeyArray) : BOOLEAN;
  688. (* Sucht den vorigen Eintrag mit gleichem Schlüssel *)
  689. VAR PrevList : DataPtr;
  690.  
  691. BEGIN
  692.   IF Index.MultipleKeys THEN
  693.    IF DataBase.RecordPtr^.Prev[Index.Type] # Empty THEN
  694.       PrevList := DataBase.RecordPtr^.Prev[Index.Type]; 
  695.       IF DataBase.Current = PrevList THEN
  696.          RETURN FALSE (* Eintrag zeigt auf sich selbst ! *)
  697.       END(*IF*);
  698.       (* Get  sollte auch ohne die Zwischenvariable PrevList funktionieren *)
  699.       (* aber sicher ist sicher *)
  700.       Get(DataBase,PrevList);
  701.       Data := DataBase.RecordPtr^.Data;
  702.       Keys := DataBase.RecordPtr^.Key;
  703.  
  704.       RETURN TRUE
  705.    ELSE
  706.       RETURN FALSE
  707.    END; (*if*)
  708.  ELSE
  709.     RETURN FALSE
  710.  END(*IF*);  
  711. END SearchPrevious;
  712.  
  713. PROCEDURE First(VAR Index, DataBase : FileOf; VAR Key : KeyType; 
  714.                  VAR Data : DataType; VAR Keys : KeyArray);
  715.  (* Ersten Eintrag in der Indexdatei ermitteln *)
  716. BEGIN
  717.   IF Tree # Empty THEN
  718.     Get ( Index,Tree);
  719.     WHILE Index.RecordPtr1^.Ptr0 # Empty DO
  720.        Get(Index,Index.RecordPtr1^.Ptr0);
  721.     END(*WHILE*);
  722.     Key:= Index.RecordPtr1^.Item[1].Key;
  723.     Keys := DataBase.RecordPtr^.Key;
  724.     Get(DataBase,Index.RecordPtr1^.Item[1].RecordNr);
  725.     Data := DataBase.RecordPtr^.Data;
  726.     (*NextList := DataBase.RecordPtr^.Next[Index.Type];*)
  727.   ELSE
  728.      Key:= 0D
  729.   END(*IF*);     
  730. END First;    
  731.  
  732. PROCEDURE Last (VAR Index, DataBase : FileOf; VAR Key : KeyType;
  733.                  VAR Data : DataType; VAR Keys : KeyArray);
  734. BEGIN
  735.   IF Tree # Empty THEN
  736.     Get (Index, Tree);    
  737.     WITH Index.RecordPtr1^ DO
  738.       WHILE Item[Anz].Ptr # Empty DO
  739.          Get(Index,Item[Anz].Ptr);
  740.       END(*WHILE*);
  741.       Get(DataBase,Item[Anz].RecordNr);
  742.       Key:= Index.RecordPtr1^.Item[Anz].Key;
  743.       Keys := DataBase.RecordPtr^.Key;
  744.     END(*WITH*);  
  745.     Data := DataBase.RecordPtr^.Data;
  746.     (*NextList := DataBase.RecordPtr^.Next[Index.Type];*)
  747.   ELSE
  748.     Key := 0D;
  749.   END(*IF*);  
  750. END Last;         
  751.  
  752. PROCEDURE Browse(Index,DataBase : FileOf; From, To : KeyType; 
  753.                  OutProc : PrintDataProc);
  754. BEGIN
  755.                  
  756. END Browse;                 
  757.  
  758.  
  759. PROCEDURE PrintTree(VAR Index, DataBase : FileOf; Tree : PagePtr; 
  760.                     Tiefe : CARDINAL; OutProc : PrintDataProc);
  761. (* Inorder durchlauf durch die B-Baumstrucktur *)
  762. (* zuerst werden alle Schlüsselwerte durchlaufen *)
  763. (* dann die Liste aller Nachfolger *)
  764. VAR i : CARDINAL;
  765.     copy : PageType;
  766. BEGIN
  767.   IF Tree # Empty THEN
  768.     Get(Index, Tree);
  769.     WITH Index.RecordPtr1^ DO
  770.         copy:=Index.RecordPtr1^;
  771.         IF Ptr0 # Empty THEN
  772.             PrintTree(Index,DataBase,Ptr0,Tiefe+1,OutProc);       
  773.             Get(Index,Tree);    
  774.         END(*IF*);    
  775.         FOR i:=1 TO Anz DO
  776.        Get(DataBase, Item[i].RecordNr);
  777.        OutProc(DataBase.RecordPtr^.Data);
  778.        IF Index.MultipleKeys THEN
  779.          WHILE DataBase.RecordPtr^.Next[Index.Type] # Empty DO
  780.            Get(DataBase,DataBase.RecordPtr^.Next[Index.Type]);
  781.            OutProc(DataBase.RecordPtr^.Data);
  782.          END(*WHILE*);        
  783.        END(*IF*);
  784.        IF copy.Item[i].Ptr # Empty THEN
  785.                PrintTree(Index,DataBase, copy.Item[i].Ptr,Tiefe+1,OutProc);
  786.                Get (Index,Tree)
  787.            END(*IF*);    
  788.     END(*FOR*);
  789.      END(*WITH*);
  790.    END(*IF*);
  791. END PrintTree;
  792.  
  793.  
  794. PROCEDURE Next(VAR Index, DataBase : FileOf; Key : KeyType; 
  795.            VAR NextKey : KeyType; VAR NextData: DataType; 
  796.            VAR Keys : KeyArray) : BOOLEAN;
  797.  
  798. CONST above = -2D;           
  799. VAR NextList : DataPtr;
  800.  
  801. PROCEDURE SearchNextPtr(VAR Index, DataBase : FileOf; Page : PagePtr; Key : KeyType) : DataPtr;
  802. (* sucht die Random-Acess-Adresse eines durch Key bezeichneten Datenbankeintrags *)
  803.     VAR q, CurrP   : PagePtr;
  804.         l, r, k    : CARDINAL;
  805.         SearchPtr0 : DataPtr;
  806.  
  807. BEGIN (* *)
  808.   REPEAT
  809.    IF Page = Empty THEN
  810.       (*SearchPtr := Empty*)
  811.       RETURN Empty;
  812.    ELSIF Page = above THEN
  813.       Page := CurrP;
  814.       IF CurrP = Tree THEN (* Ich bin wieder bei der Wurzel angekommen *)
  815.          Get(Index,CurrP);
  816.          IF r < Index.RecordPtr1^.Anz THEN
  817.             NextKey:=Index.RecordPtr1^.Item[r+1].Key;
  818.             RETURN Index.RecordPtr1^.Item[r+1].RecordNr;
  819.          ELSE
  820.             (*HALT*);
  821.             NextKey:= Index.RecordPtr1^.Item[r].Key;
  822.          END(*IF*);   
  823.      (* ******** IF k=Index.RecordPtr1^.Anz THEN (* Nur bei Bäumen die nur ein Element in der Wurzel haben! *)
  824.             NextKey:=Index.RecordPtr1^.Item[k].Key;
  825.             RETURN Index.RecordPtr1^.Item[k].RecordNr;
  826.      ELSE (* Unser Eintrag ist der Nächste auf der WurzelSeite *)
  827.            NextKey := Index.RecordPtr1^.Item[k+1].Key;
  828.            RETURN Index.RecordPtr1^.Item[k+1].RecordNr;
  829.  
  830.       END(*IF*); **** *)
  831.        ELSE (* Page hat einen Wert >= Tree *)
  832.          Get(Index,CurrP);
  833.          IF r=Index.RecordPtr1^.Anz THEN (* letzter Eintrag auf dieser Seite, der gesuchte Eintrag befindet sich noch eins höher!*)
  834.             RETURN above;
  835.          ELSE
  836.            NextKey := Index.RecordPtr1^.Item[r+1].Key; (* Endlich daheim! *)
  837.            RETURN Index.RecordPtr1^.Item[r+1].RecordNr;
  838.          END(*IF*);     
  839.        END(*IF*);     
  840.    ELSE
  841.       Get(Index,Page);
  842.       WITH Index.RecordPtr1^ DO
  843.      l:=1;
  844.      r:= Anz;
  845.      REPEAT
  846.         k := (l+r) DIV 2;
  847.         IF Key <= Item[k].Key THEN
  848.            r := k-1;
  849.         END(*IF*);
  850.         IF Key >= Item[k].Key THEN
  851.           l:= k+1;
  852.         END(*IF*);
  853.      UNTIL r<l;
  854.      IF r=0 THEN
  855.         q:= Ptr0
  856.      ELSE
  857.         q:=Item[r].Ptr;
  858.      END(*IF*);
  859.      CurrP := Page;
  860.      IF (l-r)>1 THEN (* Eintrag gefunden ! *)
  861.         (*SearchPtr:= Item[k].RecordNr*)
  862.         (*RETURN Item[k].RecordNr*)
  863.         IF Item[k].Ptr # Empty THEN (* Wir sind nicht auf einer BlattSeite *)
  864.            Get( Index, Item[k].Ptr);
  865.            WHILE Index.RecordPtr1^.Ptr0 # Empty DO (* Baum auf linker Seite runterwandern *)
  866.                Get(Index,Index.RecordPtr1^.Ptr0);
  867.            END(*WHILE*);
  868.            NextKey :=Index.RecordPtr1^.Item[1].Key;
  869.            RETURN Index.RecordPtr1^.Item[1].RecordNr;
  870.         ELSE (* Wir sind auf einer BlattSeite *)
  871.            IF k= Anz THEN   (* Letzter Eintrag auf der Blattseite *)
  872.               RETURN above; (* der nächste Eintrag ist auf einer höheren Seite *)
  873.            ELSE
  874.              NextKey := Item[k+1].Key; (* Der nächste Eintrag steht eins weiter auf dieser Blattseite !*)
  875.              RETURN Item[k+1].RecordNr;   
  876.            END(*IF*);
  877.         END(*IF*);   
  878.      ELSE (* Eintrag nicht gefunden -> Suche geht weiter *)
  879.         SearchPtr0:= SearchNextPtr(Index,DataBase,q,Key);
  880.         Page := SearchPtr0;
  881.      END(*IF*);
  882.      END; (* with*)
  883.    END; (*if*)
  884.  UNTIL Page # above;  
  885.  RETURN Page;
  886. END SearchNextPtr;
  887.  
  888. BEGIN
  889.    IF ~SearchNext(Index,DataBase,NextData,Keys) THEN
  890.        Last(Index,DataBase,NextKey,NextData,Keys);
  891.        IF NextKey = Key THEN
  892.             RETURN FALSE
  893.             (* First(Index,DataBase,NewKey,NewData);*)
  894.        ELSE
  895.        NextList:=SearchNextPtr(Index,DataBase,Tree,Key);
  896.        IF NextList # Empty THEN
  897.           Get (DataBase,NextList);
  898.           NextData := DataBase.RecordPtr^.Data;
  899.               Keys := DataBase.RecordPtr^.Key;
  900.          (* NextList := DataBase.RecordPtr^.Next[Index.Type];*)
  901.           RETURN TRUE
  902.        ELSE
  903.            (*HALT*);
  904.            RETURN FALSE (* Hier darf ich eigentlich nie hinkommen*)
  905.        END; (*if*)
  906.        END(*IF*);   
  907.     ELSE
  908.         RETURN TRUE  
  909.     END(*IF*);       
  910. END Next;
  911.  
  912. PROCEDURE Previous(VAR Index, DataBase : FileOf; Key : KeyType; 
  913.            VAR NextKey : KeyType; VAR NextData: DataType; 
  914.            VAR Keys : KeyArray) : BOOLEAN;
  915.  
  916. CONST above = -2D;           
  917. VAR NextList : DataPtr;
  918.  
  919. PROCEDURE SearchPrevPtr(VAR Index, DataBase : FileOf; Page : PagePtr; Key : KeyType) : DataPtr;
  920. (* sucht die Random-Acess-Adresse eines durch Key bezeichneten Datenbankeintrags *)
  921.     VAR q, CurrP   : PagePtr;
  922.         l, r, k    : CARDINAL;
  923.         SearchPtr0 : DataPtr;
  924.  
  925. BEGIN (* *)
  926.   REPEAT
  927.    IF Page = Empty THEN
  928.       (*SearchPtr := Empty*)
  929.       RETURN Empty;
  930.    ELSIF Page = above THEN
  931.       Page := CurrP;
  932.       IF CurrP = Tree THEN (* Ich bin wieder bei der Wurzel angekommen *)
  933.          Get(Index,CurrP);
  934.          IF r <= Index.RecordPtr1^.Anz THEN
  935.             IF r < 1  THEN
  936.                     WHILE Index.RecordPtr1^.Ptr0 # Empty DO
  937.                           Get(Index,Index.RecordPtr1^.Ptr0);
  938.                     END(*WHILE*);
  939.                     NextKey:= Index.RecordPtr1^.Item[Index.RecordPtr1^.Anz].Key;
  940.                 RETURN Index.RecordPtr1^.Item[(*1*)Index.RecordPtr1^.Anz].RecordNr;
  941.              ELSE
  942.                     NextKey := Index.RecordPtr1^.Item[r].Key;
  943.                     RETURN Index.RecordPtr1^.Item[r].RecordNr;    
  944.          END(*IF*);    
  945.          ELSE
  946.             (*HALT*);
  947.             NextKey:= Index.RecordPtr1^.Item[r-1].Key;
  948.          END(*IF*);   
  949.       ELSE (* Page hat einen Wert >= Tree *)
  950.          Get(Index,CurrP);
  951.          IF r < 1 THEN (* erster Eintrag auf dieser Seite, der gesuchte Eintrag befindet sich noch eins höher!*)
  952.             RETURN above;
  953.          ELSE
  954.            NextKey := Index.RecordPtr1^.Item[r].Key; (* Endlich daheim! *)
  955.            RETURN Index.RecordPtr1^.Item[r].RecordNr;
  956.          END(*IF*);     
  957.        END(*IF*);     
  958.    ELSE
  959.       Get(Index,Page);
  960.       WITH Index.RecordPtr1^ DO
  961.      l:=1;
  962.      r:= Anz;
  963.      REPEAT
  964.         k := (l+r) DIV 2;
  965.         IF Key <= Item[k].Key THEN
  966.            r := k-1;
  967.         END(*IF*);
  968.         IF Key >= Item[k].Key THEN
  969.           l:= k+1;
  970.         END(*IF*);
  971.      UNTIL r<l;
  972.      IF r=0 THEN
  973.         q:= Ptr0
  974.      ELSE
  975.         q:=Item[r].Ptr;
  976.      END(*IF*);
  977.      CurrP := Page;
  978.      IF (l-r)>1 THEN (* Eintrag gefunden ! *)
  979.            IF k = 1 THEN   (* Erster Eintrag auf der Seite *)
  980.                IF Ptr0 = Empty THEN (* Wir sind auf einer BlattSeite *)
  981.                     RETURN above; (* der Vorgänger ist auf einer höheren Seite *)
  982.                    ELSE (* sonst befindet sich der Vorgänger an 1. Stelle auf der darunterliegenden untersten rechten (!) Seite *)
  983.                       Get (Index,Ptr0);
  984.                       WHILE Ptr0 # Empty DO (* Ist ein Zeiger leer sin alleleer *)
  985.                           Get(Index, Item[Anz].Ptr);
  986.                       END(*WHILE*);
  987.                       NextKey := Item[Anz].Key;
  988.                       RETURN Item[(*1*) Anz].RecordNr;
  989.                    END(*IF*);   
  990.             ELSE (* k-ter Eintrag auf der Seite *)
  991.                IF Item[k].Ptr # Empty THEN (* Wir sind nicht auf einer BlattSeite *)
  992.                     Get(Index,Item[k-1].Ptr);
  993.                     WHILE Item[Anz].Ptr # Empty DO (* Baum auf rechter Seite runter wandern *)
  994.                      Get(Index,Item[Anz].Ptr);
  995.                         END(*WHILE*);
  996.                         NextKey := Item[Anz].Key;
  997.                         RETURN Item[Anz].RecordNr;
  998.                    ELSE (* Wir sind auf einer BlattSeite *)     
  999.                  NextKey := Item[k-1].Key; (* Der vorige Eintrag steht eins weiter vorn auf dieser Blattseite !*)
  1000.                  RETURN Item[k-1].RecordNr;   
  1001.                END(*IF*);  
  1002.            END(*IF*);
  1003.      ELSE (* Eintrag nicht gefunden -> Suche geht weiter *)
  1004.         SearchPtr0:= SearchPrevPtr(Index,DataBase,q,Key);
  1005.          (*RETURN SearchPtr0 *)
  1006.         Page := SearchPtr0;
  1007.      END(*IF*);
  1008.      END; (* with*)
  1009.    END; (*if*)
  1010.  UNTIL Page # above;  
  1011.  RETURN Page;
  1012. END SearchPrevPtr;
  1013.  
  1014. BEGIN
  1015.    IF ~SearchPrevious(Index,DataBase,NextData,Keys) THEN
  1016.        First(Index,DataBase,NextKey,NextData,Keys);
  1017.        IF NextKey = Key THEN (* Der erste Eintrag hat naturgemäß keinen Vorgänger *)
  1018.             RETURN FALSE
  1019.             (* Last(Index,DataBase,NewKey,NewData);*)
  1020.        ELSE
  1021.        NextList:=SearchPrevPtr(Index,DataBase,Tree,Key);
  1022.        IF NextList # Empty THEN
  1023.           Get (DataBase,NextList);
  1024.           NextData := DataBase.RecordPtr^.Data;
  1025.           NextList := DataBase.RecordPtr^.Next[Index.Type];
  1026.               Keys := DataBase.RecordPtr^.Key;
  1027.           RETURN TRUE
  1028.        ELSE
  1029.            (*HALT*);
  1030.            RETURN FALSE (* Hier darf ich eigentlich nie hinkommen*)
  1031.        END; (*if*)
  1032.        END(*IF*);   
  1033.     ELSE
  1034.         RETURN TRUE  
  1035.     END(*IF*);       
  1036. END Previous;
  1037.  
  1038. BEGIN
  1039. (* Falls Order oder PageType geändert werden kann man sich hier ohne 
  1040.    groß rumzurechnen die Größen anzeigen lassen. Sie sollten ganzahlige 
  1041.    Vielfache oder Teiler von 1024 sein wegen der Sektorgröße des GEMDOS.
  1042.    Bei anderen Größen wird entweder Plattenspeicher verschleudert oder
  1043.    die Zugriffgeschwindigkeit veringert *)
  1044. (*   
  1045.    WriteString('Size of BaseType:');
  1046.    WriteInt(TSIZE(BaseType),5);
  1047.    WriteLn;
  1048.    WriteString('Size of PageType:');
  1049.    WriteInt(TSIZE(PageType),5);
  1050.    WriteLn; 
  1051. *)    
  1052. END BTree.
  1053.